/* Copyright (c) 2015,2016 Lucky Byte, Inc.
 */
package com.lucky_byte.pay.jar;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Field
{
	private static final Logger logger = LogManager.getLogger();

	static private Map<String, Map<String, Attr>> attrs = new HashMap<>();

	static private Map<String, Attr> readAttrsFromPath(String path) {
		try {
			Properties props = new Properties();
			props.load(new FileInputStream(path));

			Map<String, Attr> prop_attrs = new HashMap<>();
			for (Entry<Object, Object> entry : props.entrySet()) {
				prop_attrs.put((String) entry.getKey(),
						new Attr(path, (String) entry.getKey(),
								(String) entry.getValue()));
			}
			attrs.put(path, prop_attrs);
			return prop_attrs;
		} catch (IOException e) {
			logger.catching(e);
			logger.error("配置文件[{}]格式错误.", path);
			return null;
		}
	}

	static public Map<String, Attr> attrsFromPath(String path) {
		if (!attrs.containsKey(path)) {
			readAttrsFromPath(path);
		}
		return attrs.get(path);
	}

	static public Map<String, Attr> attrsFromFile(File file) {
		return attrsFromPath(file.getPath());
	}


	static public class Attr
	{
		public String path;
		public String key;

		public int maxlen;
		public int llvar;
		public int llfmt;
		public String llfmt_str;
		public int fmter;
		public String fmter_str;
		public int align;
		public String align_str;
		public byte padval;
		public String regex;

		Attr(String path, String key, String value) {
			this.path = path;
			this.key = key;
			this.parseValue(value);
		}

		private void parseValue(String value) {
			String[] array = value.split(";");
			for (String entry : array) {
				String[] kv = entry.split(":", 2);
				if (kv.length < 2) {
					logger.error("文件[{}]中字段[{}]的格式错误.", path, key);
					continue;
				}
				String k = kv[0].trim();
				String v = kv[1].trim();
				switch (k) {
				case "maxlen":
					maxlen = this.getInt(k, v, 0);
					break;
				case "llvar":
					llvar = this.getInt(k, v, 0);
					break;
				case "llfmt":
					llfmt = this.getEnum(k, v, FMTER_ASC);
					llfmt_str = v;
					break;
				case "fmter":
					fmter = this.getEnum(k, v, FMTER_ASC);
					fmter_str = v;
					break;
				case "align":
					align = this.getEnum(k, v, ALIGN_LEFT);
					align_str = v;
					break;
				case "padval":
					padval = this.getByte(k, v, (byte) 32);
					align_str = v;
					break;
				case "regex":
					regex = v;
					if (regex.length() == 0 || regex.equals("nil")) {
						regex = null;
					}
					break;
				}
			}
		}

		private int getInt(String name, String value, int defval) {
			try {
				return Integer.parseInt(value);
			} catch(NumberFormatException e) {
				logger.error("文件[{}]中字段[{}]属性[{}]的值[{}]无效，请检查.",
						this.path, this.key, name, value);
				return defval;
			}
		}

		protected byte getByte(String name, String value, byte defval) {
			try {
				return Byte.parseByte(value);
			} catch(NumberFormatException e) {
				logger.error("文件[{}]中字段[{}]属性[{}]的值[{}]无效，请检查.",
						this.path, this.key, name, value);
				return defval;
			}
		}

		public static final int ALIGN_LEFT  = 1;
		public static final int ALIGN_RIGHT = 2;

		public static final int FMTER_ASC = 1;
		public static final int FMTER_BCD = 2;
		public static final int FMTER_HEX = 3;

		private final Object[][] enum_props = {
				{ "asc",    FMTER_ASC },  { "ascii",  FMTER_ASC },
				{ "bcd",    FMTER_BCD },  { "hex",    FMTER_HEX },
				{ "bin",    FMTER_HEX },  { "binary", FMTER_HEX },
				{ "left",   ALIGN_LEFT }, { "right",  ALIGN_RIGHT },
		};

		private int getEnum(String name, String value, int defval) {
			for (Object[] enum_prop : enum_props) {
				if (value.equals(enum_prop[0])) {
					return (Integer) enum_prop[1];
				}
			}
			logger.error("文件[{}]中字段[{}]属性[{}]的值[{}]无效，请检查.",
					this.path, this.key, name, value);
			return defval;
		}
	}

	/**
	 * 设置报文字段数据
	 *
	 * 数据必须符合报文规范，包括长度，内容(例如 ASCII 字段不允许设置二进制数据).
	 * 这个方法主要用于手动构造报文，例如在客户端通过终端收到数据后组装成多渠道报文.
	 *
	 * @param bytes 数据内容
	 * @param size 数据长度
	 * @return true/false
	 */
//	public static boolean setBytes(String name,
//			byte[] bytes, int size, Attrs attrs) {
//		if (bytes == null) {
//			logger.fatal("报文字段不允许设置空数据.");
//			return false;
//		}
//		if (size <= 0) {
//			size = bytes.length;
//		}
//		// 数据不能超出字段的最大容量
//		if (size > attrs.maxlen) {
//			logger.error("设置字段[{}]的数据长度[{}]超出了其最大容量[{}]字节.",
//					name, size, attrs.maxlen);
//			return false;
//		}
//		// 定长报文，其 llvar 为 0，在设置这种字段时，如果数据长度不够，
//		// 需要根据规范进行补充填充字符(左或者右).
//		if (attrs.llvar == 0) {
//			byte[] new_bytes = new byte[attrs.maxlen];
//			// 使用预定的填充字符填满，后续再根据规范填充内容.
////			byte padval = this.getPadval();
//			for (int i = 0; i < new_bytes.length; i++) {
//				new_bytes[i] = attrs.padval;
//			}
//			// 字段分左对齐和右对齐两种，内容填充的位置不同.
////			int align = this.getAlign();
//			if (attrs.align == Attrs.ALIGN_LEFT) {
//				System.arraycopy(bytes, 0, new_bytes, 0, size);
//			} else {
//				int index = attrs.maxlen - size;
//				for (int i = 0, j = index; i < size; i++, j++) {
//					new_bytes[j] = bytes[i];
//				}
//			}
//			this.bytes = new_bytes;
//			return this.verify();
//		}
//		// 如果是变长报文，则分配对应实际需要的存储空间, 这个空间不能分配多，
//		// 否则会影响输出的结果
//		this.bytes = new byte[size];
//		System.arraycopy(bytes, 0, this.bytes, 0, size);
//		return this.verify();
//	}

}
